﻿using System;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using CanvasControl.Interface;
using CanvasControl.Model;
using CanvasControl.View;
using GraphDrawing.Graph.Model;
using Appearance = CanvasControl.View.Appearance;

namespace GraphDrawing.Graph.Behaviors
{
    public class SelectionBoxBehavior : ICanvasBehavior
    {
        readonly ZOrderModel model;
        CanvasModel canvas;
        Listener listener;

        public Appearance Appearance { get; private set; }
    
        /// <summary>
        /// Создаёт новый экземпляр поведения.
        /// </summary>
        public SelectionBoxBehavior(ZOrderModel model)
        {
            this.model = model;
            Appearance = new Appearance {Color = Color.FromArgb(64, Color.Gray), Border = {Color = Color.SlateGray}};
        }

        public void Install(ICanvasView view, CanvasModel canvas)
        {
            this.canvas = canvas;
            canvas.Subscribe(listener = new Listener(model, canvas, view, Appearance));
        }

        public void Uninstall(CanvasModel canvas1)
        {
            canvas.Unsubscribe(listener);
            listener = null;
        }

        class Listener : IInputListener, IPainter
        {
            readonly ZOrderModel model;
            readonly CanvasModel canvas;
            readonly ICanvasView view;
            readonly Appearance appearance;
            SelectionModel boxState;
            

            public Listener(ZOrderModel model, CanvasModel canvas, ICanvasView view, Appearance appearance)
            {
                this.canvas = canvas;
                this.model = model;
                this.appearance = appearance;
                this.view = view;
            }

            public void OnMouseDown(InputState input)
            {
                boxState = null;
                if (input.MouseButtons != MouseButtons.Right)
                    return;
                boxState = new SelectionModel(model, view, canvas.ViewPort, input.ViewPoint);
                canvas.AddPainter(PaintStages.POSTPROCESSING, this);
            }

            public void OnMouseUp(Point p)
            {
                if (boxState == null)
                    return;
                canvas.RemovePainter(this);
                boxState = null;
            }

            public void OnMouseMove(Point p)
            {
                if (boxState != null)
                    boxState.UpdateBoxPosition(p);
            }

            public void Draw(DrawingContext dc)
            {
                var box = canvas.ViewPort.GlobalToClient(boxState.Box);
                using (var brush = appearance.CreateBrush())
                    dc.Graphics.FillRectangle(brush, box);
                using (var pen = appearance.Border.CreatePen())
                    dc.Graphics.DrawRectangle(pen, box);
            }
        }

        class SelectionModel
        {
            readonly Point originPoint;
            readonly ZOrderModel model;
            readonly ViewPortModel viewPort;
            readonly ICanvasView view;

            public Rectangle Box { get; private set; }

            /// <summary>
            /// Создаёт новый экземпляр состояния.
            /// </summary>
            /// <param name="viewPoint">Точка, в которой было начато перетаскивание.</param>
            public SelectionModel(ZOrderModel model, ICanvasView view, ViewPortModel viewPort, Point viewPoint)
            {
                this.viewPort = viewPort;
                this.model = model;
                this.view = view;
                originPoint = viewPoint;
            }

            public void UpdateBoxPosition(Point p)
            {
                var box = CalculateSelectionBox(viewPort.ClientToGlobal(p));
                model.ClearSelection();
//                foreach (var block in model.EnumerateBlocks(true).Where(b => box.IntersectsWith(b.GetBounds())))
//                    model.Select(block);
                Box = box;
                view.UpdateView();
            }

            /// <summary>
            /// Рассчитывает границы области выделения между точкой начала выделения и текущей точкой.
            /// </summary>
            /// <param name="viewPoint">Текущая точка.</param>
            Rectangle CalculateSelectionBox(Point viewPoint)
            {
                var left = Math.Min(originPoint.X, viewPoint.X);
                var top = Math.Min(originPoint.Y, viewPoint.Y);
                var width = Math.Abs(originPoint.X - viewPoint.X);
                var height = Math.Abs(originPoint.Y - viewPoint.Y);
                return new Rectangle(left, top, width, height);
            }
        }
    }
}